#ifndef _MYCONSOLE_H_
#define _MYCONSOLE_H_

#include <assert.h>
#include <map>
#include <string>
#include "Utils/Singleton.h"
#include "Utils/Util.h"
#include "Utils/Logger/Logger.h"
using namespace std;

namespace RayFrame
{

  class IConsoleValue;
  class Console;
  typedef void (*ValueCallback)(IConsoleValue* oold, IConsoleValue* nnew);

  class IConsoleValue
  {
  public:
    virtual ~IConsoleValue(){};

    virtual string GetName() = 0;
    virtual string GetDesc() = 0;

    virtual void SetValueInt() = 0;
    virtual void SetValueFloat() = 0;
    virtual void SetValueString() = 0;
    virtual void SetValueBool() = 0;

    virtual int GetValueInt() = 0;
    virtual float GetValueFloat() = 0;
    virtual string GetValueString() = 0;
    virtual bool GetValueBool() = 0;

    virtual int GetDefaultValueInt() = 0;
    virtual float GetDefaultValueFloat() = 0;
    virtual string GetDefaultValueString() = 0;
    virtual bool GetDefaultValueBool() = 0;

    virtual bool Update() = 0;
    virtual void Reset() = 0; //< Reset to default value
  };

  template<typename T>
  class ConsoleValue: public IConsoleValue
  {
    friend class Console;
  public:
    ConsoleValue(string name, string desc, T* realValue, T defaultValue, ValueCallback cb)
    {
      assert(realValue);
      m_name = name;
      m_desc = desc;
      m_realValue = realValue;
      m_value = *m_realValue;
      m_defaultValue = defaultValue;
      m_callback = cb;
    };

    ConsoleValue(ConsoleValue<T>& rhs)
    {
      this->m_name = rhs.m_name;
      this->m_desc = rhs.m_desc;
      this->m_realValue = rhs.m_realValue;
      this->m_value = rhs.m_value;
      this->m_defaultValue = rhs.m_defaultValue;
      this->m_callback = rhs.m_callback;
    };

    virtual ~ConsoleValue(){};

    virtual string GetName(){ return m_name; };
    virtual string GetDesc(){ return m_desc; };

    virtual void SetValue(int newVal)
    {
      AssignValue(m_value, newVal);
      AssignValue(*m_realValue, newVal);
    };

    virtual void SetValue(float newVal)
    {
      AssignValue(m_value, newVal);
      AssignValue(*m_realValue, newVal);
    };

    virtual void SetValueString(std::string newVal)
    {
      AssignValue(m_value, newVal);
      AssignValue(*m_realValue, newVal);
    };

    virtual void SetValueBool(bool newVal)
    {
      AssignValue(m_value, newVal);
      AssignValue(*m_realValue, newVal);
    };

    virtual int GetValueInt()  { return ToInt(m_value); };
    virtual float GetValueFloat() { return ToFloat(m_value); };
    virtual string GetValueString() { return ToString(m_value); };
    virtual bool GetValueBool() { return ToBool(m_value); };

    virtual int GetDefaultValueInt()  { return ToInt(m_defaultValue); };
    virtual float GetDefaultValueFloat() { return ToFloat(m_defaultValue); };
    virtual string GetDefaultValueString() { return ToString(m_defaultValue); };
    virtual bool GetDefaultValueBool() { return ToBool(m_defaultValue); };

    virtual bool Update()
    {
      bool result = false;
      if(m_value != *m_realValue)
      {
        if(m_callback)
        {
          ConsoleValue tmp(*this);
          tmp.m_value = *tmp.m_realValue;
          m_callback(this, &tmp);
          result = true;
        }
        m_value = *m_realValue;
      }
      return result;
    }

    virtual void Reset()
    {
      m_value = m_defaultValue;
      *m_realValue = m_defaultValue;
    }

    ConsoleValue<T>& operator = (ConsoleValue<T>& rhs)
    {
      this->m_name = rhs->m_name;
      this->m_desc = rhs->m_desc;
      this->m_realValue = rhs->m_realValue;
      this->m_value = rhs->m_value;
      this->m_defaultValue = rhs->m_defaultValue;
      this->m_callback = rhs->m_callback;
    }
  protected:
    T m_value, m_defaultValue;
    T* m_realValue;
    string m_name, m_desc;
    ValueCallback m_callback;
  };

  typedef ConsoleValue<int> ConsoleInt;

  class Console: public Singleton<Console>
  {
  public:
    DECL_SINGLETON(Console);

    Console(){};
    virtual ~Console();

    template<typename T>
    IConsoleValue* RegisterValue(string name, string desc, T* realValue,
      T defaultValue, ValueCallback callback = NULL)
    {
      if(m_values.find(name) != m_values.end())
      {
        LOG_WARNING2("Console variable with name %d registered multiple times.", name.c_str());
        return NULL;
      }

      ConsoleValue<T>* value = new ConsoleValue<T>(name, desc, realValue, defaultValue, callback);
      m_values[name] = value;
      return value;
    };

    IConsoleValue* FindValue(string name);

    void Update(double timeElapsed);
    bool Empty();
    void Clear();
  protected:
    typedef map<string, IConsoleValue*> MapValue;
    typedef MapValue::iterator IterValue;
    MapValue m_values;
  };

  class ConsoleParser
  {
  public:
    static bool Parse(std::string cmd);
    static string GetErrorMsg();
  };

}
#endif /* _MYCONSOLE_H_ */
